﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.NetworkInformation;
using System.Net;
using System.Diagnostics;

/*
     This file is part of Plogger.

    Plogger is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Plogger is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Plogger.  If not, see <http://www.gnu.org/licenses/>.
  
 */

namespace PingLogger
{
    public class Tracerter
    {
        #region Public fields.
        //Public fields.
        public string Address;
        public int MaxHops;
        public DateTime Time = DateTime.Now;
        public List<string> Result;
        #endregion

        #region Private fields

        //Private fields.
        private IPAddress IP;
        private PingOptions Options;
        private byte[] Bytes = new byte[32];
        private Stopwatch SW;
        private int TimeOut = 10000;
        private string _hn = null;

        #endregion

        #region Constructor
        public Tracerter()
        {
            this.Init();
        }

        public Tracerter(string address)
        {
            this.Init(address);
        }

        public Tracerter(string address, int hops)
        {
            this.Init(address, hops);
        }
        #endregion

        /// <summary>
        /// Method to preform the trace route and transform results.
        /// </summary>
        /// <returns>List<string></returns>
        public List<string> StartTracert()
        {
            using (Ping sender = new Ping())
            {
                string s = String.Format("Plogger tracert results{3}Performed at: {0}{3}Tracing {1} with a maximum of {2} hops.",
                            this.Time.ToString(), this.IP.ToString(), this.MaxHops.ToString(), Environment.NewLine);

                this.Result.Add(s);

                s = String.Format("Hop:\tPing 1:\tPing 2:\tPing3:\tHostname [ip]");
                this.Result.Add(s);

                for (int i = 1; i < this.MaxHops + 1; i++)
                {
                    string[] ht = new string[3];
                    string ip = null;
                    
                    for (int j = 0; j < 3; j++)
                    {
                        this.SW.Reset();
                        this.SW.Start();

                        PingReply pr = sender.Send(this.IP, this.TimeOut, this.Bytes, this.Options);

                        this.SW.Stop();

                        //Old way, but for some reason Dns.EndGetHostEntry includes some strange errors,                        
                        string bugFix = Dns.Resolve(pr.Address.ToString()).HostName.ToString();

                        if (j == 0 && this._hn == bugFix)
                            return this.Result; ;

                        if (pr.Status == IPStatus.Success || pr.Status == IPStatus.TtlExpired)
                        {
                            ht[j] = this.SW.ElapsedMilliseconds.ToString();
                            this._hn = bugFix;
                            ip = pr.Address.ToString();
                        }

                        else
                        {
                            ht[j] = "*";
                            this._hn = "timed out";
                            ip = "*";
                        }
                    }

                    s = String.Format("{0}\t{1}\t{2}\t{3}\t{4}[{5}]",
                                        i, ht[0], ht[1], ht[2], this._hn.ToString(), ip.ToString());

                    this.Result.Add(s);

                    this.Options.Ttl++;
                }
            }

            return this.Result;
        }

        /// <summary>
        /// Initialization
        /// </summary>
        /// <param name="address"></param>
        /// <param name="maxHops"></param>
        private void Init(string address = "ping.sunet.se", int maxHops = 30)
        {
            if (address.Length < 5)
                address = "ping.sunet.se";

            this.IP = Dns.GetHostAddresses(address).FirstOrDefault<IPAddress>();
            this.Options = new PingOptions();
            this.Options.DontFragment = true;
            this.Options.Ttl = 1;
            this.MaxHops = maxHops;
            this.SW = new Stopwatch();
            this.Result = new List<string>();
        }
    }
}
